.TH LOCK 3
.SH NAME
lock, canlock, unlock,
qlock, canqlock, qunlock,
rlock, canrlock, runlock,
wlock, canwlock, wunlock,
rsleep, rwakeup, rwakeupall
incref, decref
\- spin locks, queueing rendezvous locks, reader-writer locks, rendezvous points, and reference counts
.SH SYNOPSIS
.ft L
.nf
#include <u.h>
#include <libc.h>
.PP
.ft L
.nf
void	lock(Lock *l)
int	canlock(Lock *l)
void	unlock(Lock *l)
.PP
.ft L
.nf
void	qlock(QLock *l)
int	canqlock(QLock *l)
void	qunlock(QLock *l)
.PP
.ft L
.nf
void	rlock(RWLock *l)
int	canrlock(RWLock *l)
void	runlock(RWLock *l)
.PP
.ft L
.nf
void	wlock(RWLock *l)
int	canwlock(RWLock *l)
void	wunlock(RWLock *l)
.PP
.ft L
.nf
typedef struct Rendez {
	QLock *l;
	\fI...\fP
} Rendez;
.PP
.ft L
.nf
void	rsleep(Rendez *r)
int	rwakeup(Rendez *r)
int	rwakeupall(Rendez *r)
.PP
.ft L
#include <thread.h>
.PP
.ft L
.nf
typedef struct Ref {
	long ref;
} Ref;
.PP
.ft L
.nf
void incref(Ref*)
long decref(Ref*)
.fi
.SH DESCRIPTION
These routines are used  to synchronize processes sharing memory.
.PP
.B Locks
are spin locks,
.B QLocks
and
.B RWLocks
are different types of queueing locks,
and
.B Rendezes
are rendezvous points.
.PP
Locks and rendezvous points have trivial implementations in programs
not using the thread library
(see
.IR thread (3)),
since such programs have no concurrency.
.PP
Used carelessly, spin locks can be expensive and can easily generate deadlocks.
Their use is discouraged, especially in programs that use the
thread library because they prevent context switches between threads.
.PP
.I Lock
blocks until the lock has been obtained.
.I Canlock
is non-blocking.
It tries to obtain a lock and returns a non-zero value if it
was successful, 0 otherwise.
.I Unlock
releases a lock.
.PP
.B QLocks
have the same interface but are not spin locks; instead if the lock is taken
.I qlock
will suspend execution of the calling thread until it is released.
.PP
Although
.B Locks
are the more primitive lock, they have limitations; for example,
they cannot synchronize between tasks in the same
.IR proc .
Use
.B QLocks
instead.
.PP
.B RWLocks
manage access to a data structure that has distinct readers and writers.
.I Rlock
grants read access;
.I runlock
releases it.
.I Wlock
grants write access;
.I wunlock
releases it.
.I Canrlock
and
.I canwlock
are the non-blocking versions.
There may be any number of simultaneous readers,
but only one writer.
Moreover,
if write access is granted no one may have
read access until write access is released.
.PP
All types of lock should be initialized to all zeros before use; this
puts them in the unlocked state.
.PP
.B Rendezes
are rendezvous points.  Each
.B Rendez
.I r
is protected by a
.B QLock
.IB r -> l \fR,
which must be held by the callers of
.IR rsleep ,
.IR rwakeup ,
and
.IR rwakeupall .
.I Rsleep
atomically releases
.IB r -> l
and suspends execution of the calling task.
After resuming execution,
.I rsleep
will reacquire
.IB r -> l
before returning.
If any processes are sleeping on
.IR r ,
.I rwakeup
wakes one of them.
It returns 1 if a process was awakened, 0 if not.
.I Rwakeupall
wakes all processes sleeping on
.IR r ,
returning the number of processes awakened.
.I Rwakeup
and
.I rwakeupall
do not release
.IB r -> l
and do not suspend execution of the current task.
.PP
Before use,
.B Rendezes
should be initialized to all zeros except for
.IB r -> l
pointer, which should point at the
.B QLock
that will guard
.IR r .
.PP
A
.B Ref
contains a
.B long
that can be incremented and decremented atomically:
.I Incref
increments the
.I Ref
in one atomic operation.
.I Decref
atomically decrements the
.B Ref
and returns zero if the resulting value is zero, non-zero otherwise.
.SH SOURCE
.B \*9/src/lib9/qlock.c
.br
.B \*9/src/libthread
.SH BUGS
.B Locks
are not always spin locks.
Instead they are usually implemented using the 
.I pthreads
library's
.BR pthread_mutex_t ,
whose implementation method is not defined.
.PP
On
.IR pthreads -based
systems, the implementation of
.B Lock
never calls
.I pthread_mutex_destroy
to free the
.BR pthread_mutex_t 's.
This leads to resource leaks on FreeBSD 5
(though not on Linux 2.6, where
.I pthread_mutex_destroy
is a no-op).
.BR 
.PP
On systems that do not have a usable
.I pthreads
implementation, the
.B Lock
implementation provided by
.I libthread
is still not exactly a spin lock.
After each unsuccessful attempt,
.I lock
calls
.B sleep(0)
to yield the CPU; this handles the common case
where some other process holds the lock.
After a thousand unsuccessful attempts,
.I lock
sleeps for 100ms between attempts.
Another another thousand unsuccessful attempts,
.I lock
sleeps for a full second between attempts.
.B Locks
are not intended to be held for long periods of time.
The 100ms and full second sleeps are only heuristics to
avoid tying up the CPU when a process deadlocks.
As discussed above,
if a lock is to be held for much more than a few instructions,
the queueing lock types should be almost always be used.
